<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT">





  <link rel="alternate" href="/atom.xml" title="renhao" type="application/atom+xml">






<meta name="description" content="原文 TURN作为STUN 协议的一个拓展,保持了STUN的工具性质,而不作为完整的NAT传输解决方案,只提供穿透NAT的功能, 并且由具体的应用程序来使用.虽然TURN也可以独立工作,但其本身就是被设计为ICE/RFC5245 的一部分,本章就来介绍一下ICE协议的具体内容.">
<meta property="og:type" content="article">
<meta property="og:title" content="【转载】P2P通信标准协议-ICE">
<meta property="og:url" content="https://huangrenhao.gitee.io/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-ice/index.html">
<meta property="og:site_name" content="renhao">
<meta property="og:description" content="原文 TURN作为STUN 协议的一个拓展,保持了STUN的工具性质,而不作为完整的NAT传输解决方案,只提供穿透NAT的功能, 并且由具体的应用程序来使用.虽然TURN也可以独立工作,但其本身就是被设计为ICE/RFC5245 的一部分,本章就来介绍一下ICE协议的具体内容.">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2020-09-24T03:49:39.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="【转载】P2P通信标准协议-ICE">
<meta name="twitter:description" content="原文 TURN作为STUN 协议的一个拓展,保持了STUN的工具性质,而不作为完整的NAT传输解决方案,只提供穿透NAT的功能, 并且由具体的应用程序来使用.虽然TURN也可以独立工作,但其本身就是被设计为ICE/RFC5245 的一部分,本章就来介绍一下ICE协议的具体内容.">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://huangrenhao.gitee.io/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-ice/">





  <title>【转载】P2P通信标准协议-ICE | renhao</title>
  








<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css"></head>

<body itemscope="" itemtype="http://schema.org/WebPage" lang="zh-Hans">
  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope="" itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">renhao</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope="" itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://huangrenhao.gitee.io/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-ice/">

    <span hidden itemprop="author" itemscope="" itemtype="http://schema.org/Person">
      <meta itemprop="name" content="renhao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="renhao">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">【转载】P2P通信标准协议-ICE</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-07-26T09:45:40+08:00">
                2019-07-26
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope="" itemtype="http://schema.org/Thing">
                  <a href="/categories/P2P/" itemprop="url" rel="index">
                    <span itemprop="name">P2P</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-ice/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count valine-comment-count" data-xid="/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-ice/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  3.5k
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  12
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p><a href="https://evilpan.com/2015/12/20/p2p-standard-protocol-ice/" target="_blank" rel="noopener">原文</a></p>
<p>TURN作为STUN 协议的一个拓展,保持了STUN的工具性质,而不作为完整的NAT传输解决方案,只提供穿透NAT的功能, 并且由具体的应用程序来使用.虽然TURN也可以独立工作,但其本身就是被设计为<a href="http://www.rfc-editor.org/info/rfc5245" target="_blank" rel="noopener">ICE/RFC5245</a> 的一部分,本章就来介绍一下ICE协议的具体内容.<a id="more"></a></p>
<h2 id="SDP"><a href="#SDP" class="headerlink" title="SDP"></a>SDP</h2><p>ICE信息的描述格式通常采用标准的<a href="http://www.rfc-editor.org/info/rfc4566" target="_blank" rel="noopener">SDP</a>,其全称为Session Description Protocol,即会话描述协议. SDP只是一种信息格式的描述标准,不属于传输协议,但是可以被其他传输协议用来交换必要的信息,如SIP和RTSP等.</p>
<h3 id="SDP信息"><a href="#SDP信息" class="headerlink" title="SDP信息"></a>SDP信息</h3><p>一个SDP会话描述包含如下部分:</p>
<ul>
<li>会话名称和会话目的</li>
<li>会话的激活时间</li>
<li>构成会话的媒体(media)</li>
<li>为了接收该媒体所需要的信息(如地址,端口,格式等)</li>
</ul>
<p>因为在中途参与会话也许会受限制，所以可能会需要一些额外的信息:</p>
<ul>
<li>会话使用的的带宽信息</li>
<li>会话拥有者的联系信息</li>
</ul>
<p>一般来说，SDP必须包含充分的信息使得应用程序能够加入会话，并且可以提供任何非参与者使用时需要知道的资源状况，后者在当SDP同时用于多个会话声明协议时尤其有用。</p>
<h3 id="SDP格式"><a href="#SDP格式" class="headerlink" title="SDP格式"></a>SDP格式</h3><p>SDP是基于文本的协议，使用ISO 10646字符集和UTF-8编码。SDP字段名称和属性名称只使用UTF-8的一个子集US-ASCII，因此不能存在中文。虽然理论上文本字段和属性字段支持全集，但最好还是不要在其中使用中文。</p>
<p>SDP会话描述包含了多行如下类型的文本:</p>
<pre><code>&lt;type&gt;=&lt;value&gt;
</code></pre><p>其中type是大小写敏感的，其中一些行是必须要有的，有些是可选的，所有元素都必须以固定顺序给出。固定的顺序极大改善了错误检测，同时使得处理端设计更加简单。如下所示，其中可选的元素标记为* :</p>
<pre><code>会话描述:
     v=  (protocol version)
     o=  (originator and session identifier)
     s=  (session name)
     i=* (session information)
     u=* (URI of description)
     e=* (email address)
     p=* (phone number)
     c=* (connection information -- not required if included in
          all media)
     b=* (zero or more bandwidth information lines)
     One or more time descriptions (&quot;t=&quot; and &quot;r=&quot; lines; see below)
     z=* (time zone adjustments)
     k=* (encryption key)
     a=* (zero or more session attribute lines)
     Zero or more media descriptions

时间信息描述:
     t=  (time the session is active)
     r=* (zero or more repeat times)

多媒体信息描述(如果有的话):
     m=  (media name and transport address)
     i=* (media title)
     c=* (connection information -- optional if included at
          session level)
     b=* (zero or more bandwidth information lines)
     k=* (encryption key)
     a=* (zero or more media attribute lines)
</code></pre><p>所有元素的type都为小写，并且不提供拓展。但是我们可以用a(attribute)字段来提供额外的信息。一个SDP描述的例子如下:</p>
<pre><code>  v=0
  o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
  s=SDP Seminar
  i=A Seminar on the session description protocol
  u=http://www.example.com/seminars/sdp.pdf
  e=j.doe@example.com (Jane Doe)
  c=IN IP4 224.2.17.12/127
  t=2873397496 2873404696
  a=recvonly
  m=audio 49170 RTP/AVP 0
  m=video 51372 RTP/AVP 99
  a=rtpmap:99 h263-1998/90000
</code></pre><p>具体字段的type/value描述和格式可以去参考<a href="http://www.rfc-editor.org/info/rfc4566" target="_blank" rel="noopener">RFC4566</a>.</p>
<h3 id="Offer-Answer模型"><a href="#Offer-Answer模型" class="headerlink" title="Offer/Answer模型"></a>Offer/Answer模型</h3><p>上文说到，SDP用来描述多播主干网络的会话信息，但是并没有具体的交互操作细节是如何实现的，因此<a href="http://www.rfc-editor.org/info/rfc3264" target="_blank" rel="noopener">RFC3264</a> 定义了一种基于SDP的offer/answer模型。在该模型中，会话参与者的其中一方生成一个SDP报文构成offer，其中包含了一组offerer希望使用的多媒体流和编解码方法，以及offerer用来接收改数据的IP地址和端口信息。offer传输到会话的另一端(称为answerer)，由answerer生成一个answer，即用来响应对应offer的SDP报文。answer中包含不同offer对应的多媒体流，并指明该流是否可以接受。</p>
<p>RFC3264只介绍了交换数据过程，而没有定义传递offer/answer报文的方法，后者在<a href="http://www.rfc-editor.org/info/rfc3261" target="_blank" rel="noopener">RFC3261/SIP</a> 即会话初始化协议中描述。值得一提的是，offer/answer模型也经常被SIP作为一种基本方法使用。offer/answer模型在SDP报文的基础上进行了一些定义，工作过程不在此描述，需要了解细节的朋友可以参考RFC3261。</p>
<h2 id="ICE"><a href="#ICE" class="headerlink" title="ICE"></a>ICE</h2><p>ICE的全称为<strong>Interactive Connectivity Establishment</strong>，即交互式连接建立。初学者可能会将其与网络编程的ICE 弄混，其实那是不一样的东西，在网络编程中，如C++的ICE库，都是指Internate Communications Engine，是一种用于分布式程序设计的网络通信中间件。我们这里说的只是交互式连接建立。</p>
<p>ICE是一个用于在<a href="http://www.rfc-editor.org/info/rfc3264" target="_blank" rel="noopener">offer/answer</a>模式下的NAT传输协议，主要用于UDP下多媒体会话的建立，其使用了STUN协议以及TURN 协议，同时也能被其他实现了offer/answer模型的的其他程序所使用，比如<a href="http://www.rfc-editor.org/info/rfc3261" target="_blank" rel="noopener">SIP</a>（Session Initiation Protocol）。</p>
<p>使用offer/answer模型（RFC3264）的协议通常很难在NAT之间穿透，因为其目的一般是建立多媒体数据流，而且在报文中还携带了数据的源IP和端口信息，这在通过NAT时是有问题的。RFC3264还尝试在客户端之间建立直接的通路，因此中间就缺少了应用层的封装。这样设计是为了减少媒体数据延迟，减少丢包率以及减少程序部署的负担。然而这一切都很难通过NAT而完成。 有很多解决方案可以使得这些协议运行于NAT环境之中，包括<code>应用层网关(ALGs)</code>，<code>Classic STUN</code>以及<code>Realm Specific IP</code>+<code>SDP</code> 协同工作等方法。不幸的是，这些技术都是在某些网络拓扑下工作很好，而在另一些环境下表现又很差，因此我们需要一个单一的，可自由定制的解决方案，以便能在所有环境中都能较好工作。</p>
<h3 id="ICE工作流程"><a href="#ICE工作流程" class="headerlink" title="ICE工作流程"></a>ICE工作流程</h3><p>一个典型的ICE工作环境如下，有两个端点L和R，都运行在各自的NAT之后（他们自己也许并不知道），NAT的类型和性质也是未知的。 L和R通过交换<a href="http://www.rfc-editor.org/info/rfc4566" target="_blank" rel="noopener">SDP</a>信息在彼此之间建立多媒体会话，通常交换通过一个SIP服务器完成:</p>
<pre><code>                 +-----------+
                 |    SIP    |
+-------+        |    Srvr   |         +-------+
| STUN  |        |           |         | STUN  |
| Srvr  |        +-----------+         | Srvr  |
|       |        /           \         |       |
+-------+       /             \        +-------+
               /&lt;- Signaling -&gt;\
              /                 \
         +--------+          +--------+
         |  NAT   |          |  NAT   |
         +--------+          +--------+
           /                       \
          /                         \
         /                           \
     +-------+                    +-------+
     | Agent |                    | Agent |
     |   L   |                    |   R   |
     |       |                    |       |
     +-------+                    +-------+
</code></pre><p>ICE的基本思路是，每个终端都有一系列<code>传输地址</code>（包括传输协议，IP地址和端口）的候选，可以用来和其他端点进行通信。 其中可能包括:</p>
<ul>
<li>直接和网络接口联系的传输地址（host address）</li>
<li>经过NAT转换的传输地址，即反射地址（server reflective address）</li>
<li>TURN服务器分配的中继地址（relay address）</li>
</ul>
<p>虽然潜在要求任意一个L的候选地址都能用来和R的候选地址进行通信。但是实际中发现有许多组合是无法工作的。举例来说，如果L和R都在NAT之后而且不处于同一内网，他们的直接地址就无法进行通信。ICE的目的就是为了发现哪一对候选地址的组合可以工作，并且通过系统的方法对所有组合进行测试(用一种精心挑选的顺序)。</p>
<p>为了执行ICE，客户端必须要识别出其所有的地址候选，ICE中定义了三种候选类型，有些是从物理地址或者逻辑网络接口继承而来，其他则是从STUN或者TURN服务器发现的。很自然，一个可用的地址为和本地网络接口直接联系的地址，通常是内网地址， 称为<code>HOST CANDIDATE</code>，如果客户端有多个网络接口，比如既连接了WiFi又插着网线，那么就可能有多个内网地址候选。</p>
<p>其次，客户端通过STUN或者TURN来获得更多的候选传输地址，即<code>SERVER REFLEXIVE CANDIDATES</code>和<code>RELAYED CANDIDATES</code>， 如果TURN服务器是标准化的，那么两种地址都可以通过TURN服务器获得。当L获得所有的自己的候选地址之后，会将其按优先级排序，然后通过signaling通道发送到R。候选地址被存储在SDP offer报文的属性部分。当R接收到offer之后，就会进行同样的获选地址收集过程，并返回给L。</p>
<p>这一步骤之后，两个对等端都拥有了若干自己和对方的候选地址，并将其配对，组成<code>CANDIDATE PAIRS</code>。为了查看哪对组合可以工作，每个终端都进行一系列的检查。每个检查都是一次STUN request/response传输，将request从候选地址对的本地地址发送到远端地址。连接性检查的基本原则很简单:</p>
<ol>
<li>以一定的优先级将候选地址对进行排序</li>
<li>以该优先级顺序发送checks请求</li>
<li>从其他终端接收到checks的确认信息</li>
</ol>
<p>两端连接性测试,结果是一个4次握手过程:</p>
<pre><code> L                        R
 -                        -
 STUN request -&gt;             \  L&#39;s
           &lt;- STUN response  /  check

            &lt;- STUN request  \  R&#39;s
 STUN response -&gt;            /  check
</code></pre><p>值的一提的是，STUN request的发送和接收地址都是接下来进多媒体传输(如RTP和RTCP)的地址和端口，所以，客户端实际上是将STUN协议与RTP/RTCP协议在数据包中进行复用(而不是在端口上复用).</p>
<p>由于STUN Binding request用来进行连接性测试，因此STUN Binding response中会包含终端的实际地址， 如果这个地址和之前学习的所有地址都不匹配，发送方就会生成一个新的candidate，称为<code>PEER REFLEXIVE CANDIDATE</code>，和其他candidate一样，也要通过ICE的检查测试。</p>
<h3 id="连接性检查（Connectivity-Checks）"><a href="#连接性检查（Connectivity-Checks）" class="headerlink" title="连接性检查（Connectivity Checks）"></a>连接性检查（Connectivity Checks）</h3><p>所有的ICE实现都要求与STUN(RFC5389)兼容，并且废弃Classic STUN(RFC3489)。ICE的完整实现既生成checks(作为STUN client)， 也接收checks(作为STUN server)，而lite实现则只负责接收checks。这里只介绍完整实现情况下的检查过程。</p>
<p><strong>1. 为中继候选地址生成许可（Permissions）</strong></p>
<p><strong>2. 从本地候选往远端候选发送Binding Request</strong></p>
<p>在Binding请求中通常需要包含一些特殊的属性，以在ICE进行连接性检查的时候提供必要信息。</p>
<ul>
<li><p>PRIORITY 和 USE-CANDIDATE</p>
<p>终端必须在其request中包含PRIORITY属性，指明其优先级，优先级由公式计算而得。 如果有需要也可以给出特别指定的候选（即USE-CANDIDATE属性）。</p>
</li>
<li><p>ICE-CONTROLLED和ICE-CONTROLLING</p>
<p>在每次会话中，每个终端都有有两种身份，即受控方（controlled role）和主控方（controlling role）。 主控方负责选择最终用来通讯的候选地址对，受控方被告知哪个候选地址对用来进行哪次媒体流传输，并且不生成更新过的offer来提示此次告知。发起ICE处理进程（即生成offer）的一方必须是主控方，而另一方则是受控方。如果终端是受控方，那么在request中就必须加上ICE-CONTROLLED属性，同样，如果终端是主控方，就需要ICE-CONTROLLING属性。</p>
</li>
<li><p>生成Credential</p>
<p>作为连接性检查的Binding Request必须使用STUN的短期身份验证。验证的用户名被格式化为一系列username段的联结，包含了发送请求的所有对等端的用户名，以冒号隔开；密码就是对等端的密码。</p>
</li>
</ul>
<p><strong>3. 处理Response</strong></p>
<p>当收到Binding Response时，终端会将其与Binding Request相联系，通常通过事务ID。随后将会将此事务ID与 候选地址对进行绑定。</p>
<ul>
<li><p>失败响应</p>
<p>如果STUN传输返回487（Role Conflict）错误响应，终端首先会检查其是否包含了ICE-CONTROLLED或ICE-CONTROLLING 属性。如果有ICE-CONTROLLED，终端必须切换为controlling role；如果请求包含ICE-CONTROLLING属性，则必须切换为controlled role。切换好之后，终端必须使产生487错误的候选地址对进入检查队列中，并将此地址对的状态设置为<code>Waiting</code>。</p>
</li>
<li><p>成功响应，一次连接检查在满足下列所有情况时候就被认为成功:</p>
<ul>
<li>STUN传输产生一个Success Response</li>
<li>response的源IP和端口等于Binding Request的目的IP和端口</li>
<li>response的目的IP和端口等于Binding Request的源IP和端口</li>
</ul>
</li>
</ul>
<p>终端收到成功响应之后，先检查其mapped address是否与本地记录的地址对有匹配，如果没有则生成一个新的候选地址， 即对等端的反射地址。如果有匹配，则终端会构造一个可用候选地址对(valid pair)。通常很可能地址对不存在于任何 检查列表中，检索检查列表中没有被服务器反射的本地地址，这些地址把它们的本地候选转换成服务器反射地址的基地址， 并把冗余的地址去除掉。</p>
<h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>本文介绍了一种完整的NAT环境通信解决方案ICE，并且对其中涉及到的概念SDP和offer/answer模型也作了简要介绍。ICE是使用STUN/TURN工具性质的最主要协议之一，其中TURN一开始也被设计为ICE协议的一部分。值的一提的是，本文只是对这几种协议作了概述性的说明，而具体工作过程和详细的属性描述都未包含，因此如果需要根据协议来实现具体的应用程序，还需要对RFC的文档进行仔细阅读。这里给出一些参考:</p>
<ul>
<li><a href="http://www.rfc-editor.org/info/rfc5389" target="_blank" rel="noopener">stun</a></li>
<li><a href="http://www.rfc-editor.org/info/rfc5766" target="_blank" rel="noopener">turn</a></li>
<li><a href="http://www.rfc-editor.org/info/rfc5245" target="_blank" rel="noopener">ice</a></li>
<li><a href="http://www.rfc-editor.org/info/rfc4566" target="_blank" rel="noopener">sdp</a></li>
<li><a href="http://www.rfc-editor.org/info/rfc3261" target="_blank" rel="noopener">sip</a></li>
</ul>
<p>而具体的代码以及实现可以参考:</p>
<ul>
<li><a href="https://github.com/pannzh/TurnServer" target="_blank" rel="noopener">TurnServer</a></li>
<li><a href="http://www.pjsip.org/" target="_blank" rel="noopener">pjsip</a></li>
</ul>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-turn/" rel="next" title="【转载】P2P通信标准协议-TURN">
                <i class="fa fa-chevron-left"></i> 【转载】P2P通信标准协议-TURN
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/07/26/p2p-tong-xin-biao-zhun-xie-yi-sip/" rel="prev" title="【转载】通信标准协议-SIP">
                【转载】通信标准协议-SIP <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
    </div>
  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">renhao</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">94</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">19</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#SDP"><span class="nav-number">1.</span> <span class="nav-text">SDP</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#SDP信息"><span class="nav-number">1.1.</span> <span class="nav-text">SDP信息</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#SDP格式"><span class="nav-number">1.2.</span> <span class="nav-text">SDP格式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Offer-Answer模型"><span class="nav-number">1.3.</span> <span class="nav-text">Offer/Answer模型</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ICE"><span class="nav-number">2.</span> <span class="nav-text">ICE</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#ICE工作流程"><span class="nav-number">2.1.</span> <span class="nav-text">ICE工作流程</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#连接性检查（Connectivity-Checks）"><span class="nav-number">2.2.</span> <span class="nav-text">连接性检查（Connectivity Checks）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#后记"><span class="nav-number">3.</span> <span class="nav-text">后记</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">renhao</span>

  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">265.9k</span>
  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  










  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  <script src="//unpkg.com/valine/dist/Valine.min.js"></script>
  
  <script type="text/javascript">
    var GUEST = ['nick','mail','link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item=>{
      return GUEST.indexOf(item)>-1;
    });
    new Valine({
        el: '#comments' ,
        verify: false,
        notify: false,
        appId: '2P1xqUWiCoNm14MH4yhURlJi-gzGzoHsz',
        appKey: 'uJN0uagHIVgXtBO1OuLV9Ban',
        placeholder: 'Just go go',
        avatar:'mm',
        guest_info:guest,
        pageSize:'10' || 10,
    });
  </script>



  





  

  

  

  
  

  

  

  

</body>
</html>
